{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "## 7 to 8\n",
    "\n",
    "\n",
    "Gdsfactory v8, based on KLayout instead of gdstk.\n",
    "KLayout offers enhanced routing functions and additional features such DRC, dummy fill, and connectivity checks.\n",
    "\n",
    "For those still using gdsfactory v7, it is hosted on [https://github.com/gdsfactory/gdsfactory7](https://github.com/gdsfactory/gdsfactory7), along with the [documentation](https://gdsfactory.github.io/gdsfactory7/).\n",
    "\n",
    "### Benefits of Migrating:\n",
    "\n",
    "- **Integrated Data**: Ports, information, and settings are stored within the GDS file, eliminating the need for separate files.\n",
    "- **Improved Booleans**: Booleans are more robust with integer-based polygons, removing slivers of less than 1nm.\n",
    "- **Enhanced Features**: More robust booleans, DRC, LVS, and connectivity checks.\n",
    "- **Active Maintenance**: KLayout is more actively maintained with frequent updates than gdstk.\n",
    "- **Advanced Routing Algorithms**: Better routing algorithms for efficient design. Thanks to Kfactory.\n",
    "- **Grid Alignment**: Ports and polygons snap to grid by default, reducing the likelihood of 1nm gaps.\n",
    "\n",
    "### Drawbacks of Migrating:\n",
    "\n",
    "- **Potential Errors**: As with any code changes, undesired errors may be introduced. It is recommended to have regression tests for all your components before you migrate.\n",
    "- **Non-Manhattan Placement**: Slightly more difficult to define non-manhattan references, routes, or port. Manhattan Components (at 0, 90, 180 and 270) work the same way.\n",
    "- **Incomplete Functionality**: Some features, such as `route_path_length_match`, are not yet implemented.\n",
    "\n",
    "### Major Differences:\n",
    "\n",
    "- **Port units**: `port.x`, `port.x`, `port.width` have changed to Database Units (1nm by default). To set/get them in um, use `d` (decimal) e.g., `port.dxmin` `port.dcenter` or `port.dwidth` which are floats.\n",
    "- **LayerMap**: Now an Enum of integers.\n",
    "- **Routing Functions**: New functions do not require starting ports to be in the same orientation and monitor for self-intersections. `get_route` is now `route_single`, and `get_bundle` is now `route_bundle`.\n",
    "- **Grid Snapping**: All polygon points snap to grid, mitigating 1nm gaps.\n",
    "\n",
    "### Minor Differences:\n",
    "\n",
    "- Replace `from gdsfactory.cell import cell` with `from gdsfactory import cell`.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1",
   "metadata": {},
   "source": [
    "## LayerMap \n",
    "\n",
    "In v7 or below, a LayerMap needs to be called.\n",
    "\n",
    "```python\n",
    "\n",
    "from gdsfactory.technology import LayerMap\n",
    "\n",
    "class LayerMapFab(LayerMap):\n",
    "    WG = (1, 0)\n",
    "\n",
    "LAYER = LayerMapFab()\n",
    "```\n",
    "\n",
    "However in v8 it has a different type and does not need to be called.\n",
    "\n",
    "```python\n",
    "\n",
    "from gdsfactory.technology import LayerMap\n",
    "\n",
    "class LayerMapFab(LayerMap):\n",
    "    WG = (1, 0)\n",
    "\n",
    "LAYER = LayerMapFab\n",
    "```\n",
    "\n",
    "See below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "from gdsfactory.technology import LayerMap\n",
    "\n",
    "\n",
    "class LayerMapFab(LayerMap):\n",
    "    WG = (1, 0)\n",
    "\n",
    "\n",
    "LAYER = LayerMapFab\n",
    "type(LAYER)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3",
   "metadata": {},
   "outputs": [],
   "source": [
    "LAYER.WG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {},
   "outputs": [],
   "source": [
    "tuple(LAYER.WG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5",
   "metadata": {},
   "outputs": [],
   "source": [
    "str(LAYER.WG)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6",
   "metadata": {},
   "source": [
    "## Ports\n",
    "\n",
    "Ports are now stored in the GDS file and are not stored in a separate file.\n",
    "\n",
    "Ports are snapped to grid, and therefore, width, x and y is in DBU (1nm by default)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.components.straight(length=10)\n",
    "print(c.ports[\"o2\"].width, \"in DBU\")\n",
    "print(c.ports[\"o2\"].dwidth, \"in um\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(c.ports[\"o2\"].dx, \"in DBU\")\n",
    "print(c.ports[\"o2\"].x, \"in um\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "## Component.bbox is now a function\n",
    "\n",
    "Both Components and Instances have a `bbox` and `dbbox` that are now functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.c.straight(length=10)\n",
    "c.bbox()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "dir(c.dbbox())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "c.dbbox().right"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13",
   "metadata": {},
   "source": [
    "For the old bbox behavior you can use `c.bbox_np` which returns the bbox as a numpy array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "c.bbox_np()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15",
   "metadata": {},
   "source": [
    "## Component.get_polygons()\n",
    "\n",
    "`Component.get_polygons()` returns all the Polygons and can be slow.\n",
    "\n",
    "`Component.get_polygons_points()` is the equivalent to `Component.get_polygons()` in gdsfactory7 where we return the polygon edges.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "\n",
    "c = gf.pack([gf.components.seal_ring_segmented()] * 200)[0]\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "%time\n",
    "p = c.get_polygons_points()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {},
   "source": [
    "If you only care about the polygons from one layer you can also only extract those."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "%time\n",
    "p = c.get_polygons_points(layers=(\"M3\",))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20",
   "metadata": {},
   "source": [
    "## ref.get_ports_list\n",
    "\n",
    "Reference does not have `get_ports_list`. You can use\n",
    "\n",
    "- ref.ports to get all ports\n",
    "- ref.ports.filter(...) to filter all ports with angle, port_type, orientation ...\n",
    "- gf.port.get_ports_list(b, ...) for backwards compatibility."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "\n",
    "c = gf.Component()\n",
    "bend = gf.components.bend_euler()\n",
    "b = c.add_ref(bend)\n",
    "\n",
    "b.ports.filter(orientation=90)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22",
   "metadata": {},
   "outputs": [],
   "source": [
    "gf.port.get_ports_list(b, orientation=90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23",
   "metadata": {},
   "source": [
    "## Routing functions\n",
    "\n",
    "Routing functions NO longer return the route Instances but they place the instances in a Component, so you have to pass a Component as the first argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.Component()\n",
    "w = gf.components.straight(cross_section=\"rib\")\n",
    "top = c << w\n",
    "bot = c << w\n",
    "bot.move((0, -2))\n",
    "\n",
    "p0 = top.ports[\"o2\"]\n",
    "p1 = bot.ports[\"o2\"]\n",
    "\n",
    "r = gf.routing.route_single(\n",
    "    c,\n",
    "    p0,\n",
    "    p1,\n",
    "    cross_section=\"rib\",\n",
    ")\n",
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25",
   "metadata": {},
   "source": [
    "🚀 The new routing functions allow the starting ports `ports1` to have different orientations.\n",
    "\n",
    "The end ports `ports2` still require to have the same orientation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.Component()\n",
    "top = c << gf.components.nxn(north=8, south=0, east=0, west=0)\n",
    "bot = c << gf.components.nxn(north=2, south=2, east=2, west=2, xsize=10, ysize=10)\n",
    "top.movey(100)\n",
    "\n",
    "routes = gf.routing.route_bundle(\n",
    "    c,\n",
    "    ports1=bot.ports,\n",
    "    ports2=top.ports,\n",
    "    radius=5,\n",
    "    sort_ports=True,\n",
    "    cross_section=\"strip\",\n",
    ")\n",
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27",
   "metadata": {},
   "source": [
    "## Built in connectivity checks\n",
    "\n",
    "gdsfactory8 includes connectivity checks to ensure things are properly connected. no\n",
    "\n",
    "This can help you find any:\n",
    "\n",
    "- Gaps between ports.\n",
    "- Width mismatches.\n",
    "- Unconnected ports.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = gf.Component()\n",
    "s = c << gf.components.straight(width=1)\n",
    "\n",
    "b1 = c << gf.components.bend_euler()\n",
    "b1.connect(\"o1\", s[\"o2\"], allow_width_mismatch=True)\n",
    "\n",
    "b2 = c << gf.components.bend_euler(radius=5)\n",
    "b2.connect(\"o1\", s[\"o1\"], allow_width_mismatch=True)\n",
    "\n",
    "# Grating Coupler: This is a device on a photonic chip that acts as an interface, \n",
    "# scattering light to allow it to travel between an optical fiber (off-chip) and a waveguide (on-chip).\n",
    "# TE: This stands for Transverse-Electric, which refers to a specific polarization of light. \n",
    "# This coupler is optimized to work efficiently with TE-polarized light.\n",
    "gc = gf.components.grating_coupler_elliptical_te()\n",
    "gc1 = c << gc\n",
    "gc2 = c << gc\n",
    "\n",
    "# Two grating couplers (gc1, gc2) are added. They are then connected to the open ends of the bends, completing the U-shaped path for the light.\n",
    "gc1.connect(\"o1\", b1.ports[\"o2\"])\n",
    "gc2.connect(\"o1\", b2.ports[\"o2\"])\n",
    "\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29",
   "metadata": {},
   "outputs": [],
   "source": [
    "# The connectivity_check function is run on the component c. \n",
    "# It searches for all ports of the specified types (\"optical\", \"electrical\") that are not connected to another port. \n",
    "# The locations of these unspecified ports are stored in the lyrdb object.\n",
    "lyrdb = c.connectivity_check(port_types=(\"optical\", \"electrical\"))\n",
    "\n",
    "# The results of the check are saved to a file named errors.lyrdb. \n",
    "# This is a KLayout Layer Properties file that contains instructions on how to draw markers at the locations of the unconnected ports.\n",
    "filepath = gf.config.home / \"errors.lyrdb\"\n",
    "lyrdb.save(filepath)\n",
    "\n",
    "# The gf.show function opens the component c in the KLayout viewer. The lyrdb=filepath argument tells KLayout to also load the error report file. \n",
    "# This causes KLayout to place visual markers (often red squares or circles) directly on the layout at the exact location of each unconnected port,\n",
    "# making them easy to find and fix.\n",
    "gf.show(c, lyrdb=filepath)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30",
   "metadata": {},
   "source": [
    "## Metadata\n",
    "\n",
    "GDSFactory adds metadata to the gds files to store the settings and the ports. If you are using a different EDA tool, you disable the metadata by setting `with_metadata=False` when you run `write_gds`.\n",
    "\n",
    "```python\n",
    "import gdsfactory as gf\n",
    "\n",
    "c = gf.components.mzi()\n",
    "c.write_gds(\"mzi.gds\", with_metadata=False)\n",
    "```\n"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "custom_cell_magics": "kql"
  },
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
